package edu.uic.cs.cs582.process.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import edu.mit.jwi.item.POS;
import edu.uic.cs.cs582.data.Component;
import edu.uic.cs.cs582.data.Polarity;
import edu.uic.cs.cs582.data.Synset;
import edu.uic.cs.cs582.data.SynsetSet;
import edu.uic.cs.cs582.data.Word;
import edu.uic.cs.cs582.misc.Assert;
import edu.uic.cs.cs582.misc.GeneralException;
import edu.uic.cs.cs582.process.CnfGenerator;

public class MajoritySynsetsCnfGeneratorImpl extends
		AbstractVariablesOperations implements CnfGenerator
{
	public MajoritySynsetsCnfGeneratorImpl(Component component)
	{
		super(component);
	}

	@Override
	public Collection<int[]> generateCnfs(POS pos, Word word, Polarity polarity)
	{
		Collection<int[]> wordExpressionCnfs = null;
		if (word.isAlreadyStoredInDisk())
		{
			wordExpressionCnfs = FileStoreHelper.getCnfFromDisk(word, pos);
		}
		else
		{
			switch (polarity)
			{
				case POSITIVE:
					wordExpressionCnfs = generatePositiveWordExpression(word);
					break;
				case NEGATIVE:
					wordExpressionCnfs = generateNegativeWordExpression(word);
					break;
				case NEUTRAL:
					wordExpressionCnfs = generateNeutralWordExpression(word);
					break;
				default:
					// DO NOT have NEUTRAL symbols in clause other than basic terms
					throw new GeneralException("Can not be here! ");
			}

			if (word.willStoreInDisk())
			{
				FileStoreHelper.storeToDisk(word, pos, wordExpressionCnfs);
			}
		}

		checkGeneratedCnfs(word, polarity, wordExpressionCnfs);

		return wordExpressionCnfs;

	}

	protected void checkGeneratedCnfs(Word word, Polarity polarity,
			Collection<int[]> wordExpressionCnfs)
	{
		Assert.notNull(wordExpressionCnfs, "No CNF generated for word - "
				+ word + "[" + polarity + "]! ");
	}

	/**
	 * NOTICE! the cnf converter we are using can only convert 'NOT' with basic variable.
	 * Like this: NOT v11; NOT(AND/OR...) is illegal!
	 * So, we need convert it!
	 * 
	 * NOT ( (v11 AND v21) OR (v11 AND v31) OR (v21 AND v31 AND v41) )
	 * AND
	 * NOT ( (v12 AND v22) OR (v12 AND v32) OR (v22 AND v32 AND v42) )
	 * |
	 * |
	 * NOT(v11 AND v21) AND NOT(v11 AND v31) AND NOT(v21 AND v31 AND v41)
	 * AND
	 * NOT(v12 AND v22) AND NOT(v12 AND v32) AND NOT(v22 AND v32 AND v42)
	 * |
	 * |
	 * (NOT v11 OR NOT v21) AND (NOT v11 OR NOT v31) AND (NOT v21 OR NOT v31 OR NOT v41)
	 * AND
	 * (NOT v12 OR NOT v22) AND (NOT v12 OR NOT v32) AND (NOT v22 OR NOT v32 OR NOT v42)
	 */
	protected Set<int[]> generateNeutralWordExpression(Word word)
	{
		Set<int[]> notPositive = generatePositiveWordNotExpression(word);
		Set<int[]> notNegative = generateNegativeWordNotExpression(word);

		notPositive.addAll(notNegative);

		return notPositive;
	}

	/**
	 * (NOT v12 OR NOT v22) AND (NOT v12 OR NOT v32) AND (NOT v22 OR NOT v32 OR NOT v42)
	 */
	protected Set<int[]> generateNegativeWordNotExpression(Word word)
	{
		List<SynsetSet> majoritySynsets = word.getMajoritySynsets();
		TreeSet<int[]> cnfs = new TreeSet<int[]>(new IntArrayComparator());

		for (int index = 0; index < majoritySynsets.size(); index++)
		{
			int[] clause = convertNotNegativeToLogicFormula(majoritySynsets
					.get(index));
			cnfs.add(clause);
		}

		return cnfs;

	}

	private int[] convertNotNegativeToLogicFormula(
			Set<Synset> onePossibleCombination)
	{
		int[] result = new int[onePossibleCombination.size()];
		int index = 0;
		for (Synset synset : onePossibleCombination)
		{
			result[index++] = not(negativeSymbol(indexOf(synset)));
		}

		Arrays.sort(result);

		return result;
	}

	protected int indexOf(Synset synset)
	{
		return component.getIndexOfSynset(synset);
	}

	/**
	 * (NOT v11 OR NOT v21) AND (NOT v11 OR NOT v31) AND (NOT v21 OR NOT v31 OR NOT v41)
	 */
	protected Set<int[]> generatePositiveWordNotExpression(Word word)
	{
		List<SynsetSet> majoritySynsets = word.getMajoritySynsets();
		TreeSet<int[]> cnfs = new TreeSet<int[]>(new IntArrayComparator());

		for (int index = 0; index < majoritySynsets.size(); index++)
		{
			int[] clause = convertNotPositiveToLogicFormula(majoritySynsets
					.get(index));
			cnfs.add(clause);
		}

		return cnfs;
	}

	private int[] convertNotPositiveToLogicFormula(
			Set<Synset> onePossibleCombination)
	{
		int[] result = new int[onePossibleCombination.size()];
		int index = 0;
		for (Synset synset : onePossibleCombination)
		{
			result[index++] = not(positiveSymbol(indexOf(synset)));
		}

		Arrays.sort(result);

		return result;
	}

	protected List<int[]> generateNegativeWordExpression(Word word)
	{
		List<SynsetSet> majoritySynsets = word.getMajoritySynsets();
		List<int[]> dnfList = new ArrayList<int[]>(majoritySynsets.size());

		for (int index = 0; index < majoritySynsets.size(); index++)
		{
			int[] clause = convertNegativeToDnfClause(majoritySynsets
					.get(index));
			dnfList.add(clause);
		}

		return convertDnfToCnf(dnfList);

	}

	private int[] convertNegativeToDnfClause(Set<Synset> onePossibleCombination)
	{
		int[] result = new int[onePossibleCombination.size()];
		int index = 0;
		for (Synset synset : onePossibleCombination)
		{
			result[index++] = negativeSymbol(indexOf(synset));
		}

		Arrays.sort(result);

		return result;
	}

	protected List<int[]> generatePositiveWordExpression(Word word)
	{
		List<SynsetSet> majoritySynsets = word.getMajoritySynsets();
		List<int[]> dnfList = new ArrayList<int[]>(majoritySynsets.size());

		for (int index = 0; index < majoritySynsets.size(); index++)
		{
			int[] clause = convertPositiveToDnfClause(majoritySynsets
					.get(index));
			dnfList.add(clause);
		}

		return convertDnfToCnf(dnfList);
	}

	private int[] convertPositiveToDnfClause(Set<Synset> onePossibleCombination)
	{
		int[] result = new int[onePossibleCombination.size()];
		int index = 0;
		for (Synset synset : onePossibleCombination)
		{
			result[index++] = positiveSymbol(indexOf(synset));
		}

		Arrays.sort(result);

		return result;
	}

}
